import tkinter as tk
from tkinter import ttk, messagebox, simpledialog, filedialog
import threading
import socket
import time
import json
from datetime import datetime, timedelta
import os
import matplotlib
matplotlib.use("TkAgg")  # 使用Tkinter后端
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import numpy as np

class NetworkMonitorApp:
    def __init__(self, root):
        self.root = root
        self.root.title("网络监控工具")
        self.root.geometry("1200x800")  # 增加窗口大小以容纳图表
        self.root.minsize(1000, 700)
        
        # 设置中文字体
        self.style = ttk.Style()
        self.style.configure("TLabel", font=("SimHei", 10))
        self.style.configure("TButton", font=("SimHei", 10))
        self.style.configure("TTreeview", font=("SimHei", 10))
        self.style.configure("TNotebook.Tab", font=("SimHei", 10))
        
        # 服务器列表和状态
        self.servers = []
        self.server_status = {}
        self.server_logs = {}  # 存储服务器连接日志
        self.server_history = {}  # 存储服务器历史连接状态
        self.monitoring = False
        self.monitor_thread = None
        
        # 配置文件路径
        self.config_file = "servers.json"
        
        # 创建日志目录
        self.log_dir = "server_logs"
        self.ensure_log_directory_exists()
        
        # 配置matplotlib中文字体
        self.configure_matplotlib_fonts()
        
        # 创建UI
        self.create_widgets()
        
        # 尝试加载保存的服务器
        self.load_servers()
        
        # 启动监控
        self.start_monitoring()
    
    def configure_matplotlib_fonts(self):
        """配置matplotlib支持中文显示"""
        # 设置中文字体
        matplotlib.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
        matplotlib.rcParams["axes.unicode_minus"] = False  # 正确显示负号
    
    def ensure_log_directory_exists(self):
        """确保日志目录存在"""
        if not os.path.exists(self.log_dir):
            os.makedirs(self.log_dir)
    
    def get_log_file_path(self, server, port):
        """获取服务器日志文件路径"""
        # 替换IP或域名中的点为下划线，避免文件名问题
        safe_server = server.replace(".", "_")
        return os.path.join(self.log_dir, f"{safe_server}_{port}.txt")
    
    def save_log_to_file(self, server, port, log_entry):
        """将日志条目保存到文件"""
        file_path = self.get_log_file_path(server, port)
        try:
            with open(file_path, "a", encoding="utf-8") as f:
                # 格式化日志条目
                timestamp = log_entry["timestamp"]
                status = log_entry["status"]
                response_time = log_entry["response_time"]
                error = log_entry["error"]
                
                log_line = f"{timestamp} - {status} ({response_time})"
                if error:
                    log_line += f" - 错误: {error}"
                log_line += "\n"
                
                f.write(log_line)
        except Exception as e:
            print(f"保存日志到文件失败: {e}")
    
    def create_widgets(self):
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding=10)
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 创建顶部按钮框架
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 添加服务器按钮
        ttk.Button(button_frame, text="添加服务器", command=self.add_server).pack(side=tk.LEFT, padx=(0, 5))
        
        # 删除服务器按钮
        ttk.Button(button_frame, text="删除选中服务器", command=self.remove_server).pack(side=tk.LEFT, padx=(0, 5))
        
        # 导入/导出配置按钮
        ttk.Button(button_frame, text="导入配置", command=self.import_config).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(button_frame, text="导出配置", command=self.export_config).pack(side=tk.LEFT, padx=(0, 5))
        
        # 报表按钮
        ttk.Button(button_frame, text="查看报表", command=self.show_reports).pack(side=tk.LEFT, padx=(0, 5))
        
        # 手动刷新按钮
        ttk.Button(button_frame, text="立即刷新", command=self.refresh_status).pack(side=tk.LEFT, padx=(0, 5))
        
        # 打开日志文件夹按钮
        ttk.Button(button_frame, text="打开日志文件夹", command=self.open_log_directory).pack(side=tk.LEFT, padx=(0, 5))
        
        # 监控控制按钮
        self.monitor_button = ttk.Button(button_frame, text="停止监控", command=self.toggle_monitoring)
        self.monitor_button.pack(side=tk.RIGHT)
        
        # 创建状态标签
        self.status_label = ttk.Label(main_frame, text="监控状态: 正在监控")
        self.status_label.pack(fill=tk.X, pady=(0, 5))
        
        # 创建服务器列表表格
        columns = ("服务器", "端口", "状态", "响应时间", "最后检查时间", "解析IP")
        self.server_tree = ttk.Treeview(main_frame, columns=columns, show="headings")
        
        # 设置列标题
        for col in columns:
            self.server_tree.heading(col, text=col)
            self.server_tree.column(col, width=150, anchor=tk.CENTER)
        
        # 调整列宽
        self.server_tree.column("服务器", width=150)
        self.server_tree.column("端口", width=80)
        self.server_tree.column("状态", width=100)
        self.server_tree.column("响应时间", width=120)
        self.server_tree.column("最后检查时间", width=180)
        self.server_tree.column("解析IP", width=150)  # 新增解析IP列
        
        self.server_tree.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
        
        # 绑定选择事件
        self.server_tree.bind("<<TreeviewSelect>>", self.on_server_select)
        
        # 创建日志区域
        log_frame = ttk.LabelFrame(main_frame, text="连接日志")
        log_frame.pack(fill=tk.BOTH, expand=True)
        
        # 创建日志文本框
        self.log_text = tk.Text(log_frame, wrap=tk.WORD, state=tk.DISABLED)
        self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        # 创建滚动条
        scrollbar = ttk.Scrollbar(log_frame, command=self.log_text.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.log_text.config(yscrollcommand=scrollbar.set)
        
        # 创建状态栏
        self.status_bar = ttk.Label(main_frame, text="就绪", relief=tk.SUNKEN, anchor=tk.W)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
    
    def open_log_directory(self):
        """打开日志文件夹"""
        try:
            if os.name == 'nt':  # Windows
                os.startfile(self.log_dir)
            elif os.name == 'posix':  # macOS/Linux
                os.system(f'open "{self.log_dir}"')
            else:
                messagebox.showinfo("提示", f"日志文件夹位置: {os.path.abspath(self.log_dir)}")
        except Exception as e:
            messagebox.showerror("错误", f"无法打开日志文件夹: {e}")
    
    def add_server(self):
        # 创建添加服务器对话框
        server_dialog = tk.Toplevel(self.root)
        server_dialog.title("添加服务器")
        server_dialog.geometry("360x200")
        server_dialog.transient(self.root)
        server_dialog.grab_set()
        
        # 计算对话框居中位置
        self.root.update_idletasks()
        x = self.root.winfo_x() + (self.root.winfo_width() // 2) - (360 // 2)
        y = self.root.winfo_y() + (self.root.winfo_height() // 2) - (200 // 2)
        server_dialog.geometry(f"+{x}+{y}")
        
        # 创建表单
        form_frame = ttk.Frame(server_dialog, padding=10)
        form_frame.pack(fill=tk.BOTH, expand=True)
        
        # 服务器地址
        ttk.Label(form_frame, text="服务器地址(IP或域名):").grid(row=0, column=0, sticky=tk.W, pady=5)
        server_entry = ttk.Entry(form_frame, width=30)
        server_entry.grid(row=0, column=1, pady=5)
        
        # 端口
        ttk.Label(form_frame, text="端口:").grid(row=1, column=0, sticky=tk.W, pady=5)
        port_entry = ttk.Entry(form_frame, width=30)
        port_entry.grid(row=1, column=1, pady=5)
        
        # 备注
        ttk.Label(form_frame, text="备注:").grid(row=2, column=0, sticky=tk.W, pady=5)
        remark_entry = ttk.Entry(form_frame, width=30)
        remark_entry.grid(row=2, column=1, pady=5)
        
        # 提交按钮
        def submit():
            server = server_entry.get().strip()
            port = port_entry.get().strip()
            remark = remark_entry.get().strip()
            
            if not server or not port:
                messagebox.showerror("错误", "服务器地址和端口不能为空")
                return
            
            try:
                port = int(port)
                if port < 1 or port > 65535:
                    raise ValueError
            except ValueError:
                messagebox.showerror("错误", "端口必须是1-65535之间的整数")
                return
            
            # 添加服务器
            self.servers.append({"server": server, "port": port, "remark": remark})
            self.update_server_tree()
            self.save_servers()
            server_dialog.destroy()
        
        ttk.Button(form_frame, text="添加", command=submit).grid(row=3, column=0, pady=10)
        ttk.Button(form_frame, text="取消", command=server_dialog.destroy).grid(row=3, column=1, pady=10)
    
    def remove_server(self):
        selected_item = self.server_tree.selection()
        if not selected_item:
            messagebox.showinfo("提示", "请先选择要删除的服务器")
            return
        
        if messagebox.askyesno("确认", "确定要删除选中的服务器吗?"):
            for item in selected_item:
                index = int(item)
                if 0 <= index < len(self.servers):
                    server = self.servers[index]
                    server_key = (server["server"], server["port"])
                    # 删除服务器时也删除对应的日志
                    if server_key in self.server_logs:
                        del self.server_logs[server_key]
                    # 删除对应的日志文件
                    try:
                        log_file = self.get_log_file_path(server["server"], server["port"])
                        if os.path.exists(log_file):
                            os.remove(log_file)
                    except Exception as e:
                        print(f"删除日志文件失败: {e}")
                    # 删除历史数据
                    if server_key in self.server_history:
                        del self.server_history[server_key]
                    del self.servers[index]
            
            self.update_server_tree()
            self.save_servers()
            # 清空日志显示
            self.clear_logs()
    
    def update_server_tree(self):
        # 清空现有项
        for item in self.server_tree.get_children():
            self.server_tree.delete(item)
        
        # 添加服务器到表格
        for i, server in enumerate(self.servers):
            server_addr = server["server"]
            port = server["port"]
            remark = server.get("remark", "")
            display_name = f"{server_addr} ({remark})" if remark else server_addr
            
            status_info = self.server_status.get((server_addr, port), {})
            status = status_info.get("status", "未知")
            response_time = status_info.get("response_time", "N/A")
            last_checked = status_info.get("last_checked", "N/A")
            resolved_ip = status_info.get("resolved_ip", "未解析")
            
            # 设置状态颜色
            if status == "在线":
                status_color = "green"
            elif status == "离线":
                status_color = "red"
            else:
                status_color = "gray"
            
            # 添加到表格
            item = self.server_tree.insert("", tk.END, iid=str(i), values=(
                display_name, port, status, response_time, last_checked, resolved_ip
            ))
            self.server_tree.item(item, tags=(status,))
        
        # 配置标签样式
        self.server_tree.tag_configure("在线", foreground="green")
        self.server_tree.tag_configure("离线", foreground="red")
        self.server_tree.tag_configure("未知", foreground="gray")
    
    def resolve_domain(self, host):
        """解析域名到IP地址"""
        try:
            # 尝试解析域名
            ip = socket.gethostbyname(host)
            return ip
        except socket.gaierror as e:
            # 解析失败
            return None
    
    def check_server_status(self, server, port):
        start_time = time.time()
        status = "离线"
        resolved_ip = None
        
        # 解析域名
        resolved_ip = self.resolve_domain(server)
        if not resolved_ip:
            error_msg = f"域名解析失败: {server}"
        else:
            try:
                with socket.create_connection((resolved_ip, port), timeout=2):
                    status = "在线"
            except (socket.error, OSError) as e:
                status = "离线"
                error_msg = str(e)
            else:
                error_msg = ""
        
        end_time = time.time()
        
        response_time = f"{(end_time - start_time) * 1000:.2f} ms"
        last_checked = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        
        # 记录日志
        log_entry = {
            "timestamp": last_checked,
            "status": status,
            "response_time": response_time,
            "error": error_msg,
            "resolved_ip": resolved_ip
        }
        
        # 保存历史数据 (用于报表)
        server_key = (server, port)
        if server_key not in self.server_history:
            self.server_history[server_key] = []
        
        # 将时间戳转换为datetime对象用于排序
        timestamp_obj = datetime.strptime(last_checked, "%Y-%m-%d %H:%M:%S")
        self.server_history[server_key].append({
            "timestamp": timestamp_obj,
            "status": 1 if status == "在线" else 0,  # 1表示在线，0表示离线
            "response_time": float(response_time.split()[0])  # 提取响应时间数值
        })
        
        # 限制历史数据数量，只保留最近的100条
        if len(self.server_history[server_key]) > 100:
            self.server_history[server_key] = self.server_history[server_key][-100:]
        
        # 将日志添加到对应服务器的日志列表中
        if server_key not in self.server_logs:
            self.server_logs[server_key] = []
        self.server_logs[server_key].append(log_entry)
        
        # 保存日志到文件
        self.save_log_to_file(server, port, log_entry)
        
        # 限制日志数量，只保留最近的50条
        if len(self.server_logs[server_key]) > 50:
            self.server_logs[server_key] = self.server_logs[server_key][-50:]
        
        return {
            "status": status,
            "response_time": response_time,
            "last_checked": last_checked,
            "resolved_ip": resolved_ip
        }
    
    def monitor_servers(self):
        while self.monitoring:
            for server in self.servers:
                server_addr = server["server"]
                port = server["port"]
                result = self.check_server_status(server_addr, port)
                self.server_status[(server_addr, port)] = result
                
                # 更新UI
                self.root.after(0, self.update_server_tree)
                
                # 如果当前选中的是该服务器，则更新日志显示
                selected_item = self.server_tree.selection()
                if selected_item:
                    selected_index = int(selected_item[0])
                    if 0 <= selected_index < len(self.servers):
                        selected_server = self.servers[selected_index]
                        if (selected_server["server"] == server_addr and 
                            selected_server["port"] == port):
                            self.root.after(0, self.display_server_logs, server_addr, port)
            
            # 等待一段时间再检查
            time.sleep(5)
    
    def start_monitoring(self):
        if not self.monitoring:
            self.monitoring = True
            self.monitor_thread = threading.Thread(target=self.monitor_servers)
            self.monitor_thread.daemon = True
            self.monitor_thread.start()
            self.status_label.config(text="监控状态: 正在监控")
            self.monitor_button.config(text="停止监控")
    
    def stop_monitoring(self):
        if self.monitoring:
            self.monitoring = False
            if self.monitor_thread:
                self.monitor_thread.join(timeout=1.0)
            self.status_label.config(text="监控状态: 已停止")
            self.monitor_button.config(text="开始监控")
    
    def toggle_monitoring(self):
        if self.monitoring:
            self.stop_monitoring()
        else:
            self.start_monitoring()
    
    def refresh_status(self):
        # 更新状态标签
        self.status_bar.config(text="正在刷新...")
        self.root.update()
        
        # 创建线程进行刷新
        refresh_thread = threading.Thread(target=self._refresh_status_thread)
        refresh_thread.daemon = True
        refresh_thread.start()
    
    def _refresh_status_thread(self):
        for server in self.servers:
            server_addr = server["server"]
            port = server["port"]
            result = self.check_server_status(server_addr, port)
            self.server_status[(server_addr, port)] = result
            
            # 更新UI
            self.root.after(0, self.update_server_tree)
            
            # 如果当前选中的是该服务器，则更新日志显示
            selected_item = self.server_tree.selection()
            if selected_item:
                selected_index = int(selected_item[0])
                if 0 <= selected_index < len(self.servers):
                    selected_server = self.servers[selected_index]
                    if (selected_server["server"] == server_addr and 
                        selected_server["port"] == port):
                        self.root.after(0, self.display_server_logs, server_addr, port)
        
        # 更新状态栏
        self.root.after(0, lambda: self.status_bar.config(text="刷新完成"))
    
    def save_servers(self):
        """保存服务器配置到JSON文件"""
        try:
            with open(self.config_file, "w", encoding="utf-8") as f:
                json.dump(self.servers, f, ensure_ascii=False, indent=2)
        except Exception as e:
            print(f"保存服务器列表失败: {e}")
    
    def load_servers(self):
        """从JSON文件加载服务器配置"""
        try:
            if os.path.exists(self.config_file):
                with open(self.config_file, "r", encoding="utf-8") as f:
                    self.servers = json.load(f)
                self.update_server_tree()
        except (FileNotFoundError, json.JSONDecodeError) as e:
            print(f"加载服务器配置失败: {e}")
            self.servers = []
    
    def export_config(self):
        """导出服务器配置到JSON文件"""
        try:
            file_path = filedialog.asksaveasfilename(
                defaultextension=".json",
                filetypes=[("JSON files", "*.json"), ("All files", "*.*")],
                title="导出服务器配置"
            )
            
            if file_path:
                with open(file_path, "w", encoding="utf-8") as f:
                    json.dump(self.servers, f, ensure_ascii=False, indent=2)
                messagebox.showinfo("成功", f"服务器配置已导出到: {file_path}")
        except Exception as e:
            messagebox.showerror("错误", f"导出配置失败: {e}")
    
    def import_config(self):
        """从JSON文件导入服务器配置"""
        try:
            file_path = filedialog.askopenfilename(
                filetypes=[("JSON files", "*.json"), ("All files", "*.*")],
                title="导入服务器配置"
            )
            
            if file_path:
                with open(file_path, "r", encoding="utf-8") as f:
                    new_servers = json.load(f)
                
                # 验证导入的配置格式
                valid_servers = []
                for server in new_servers:
                    if isinstance(server, dict) and "server" in server and "port" in server:
                        valid_servers.append(server)
                
                if not valid_servers:
                    messagebox.showerror("错误", "导入的配置文件格式不正确")
                    return
                
                # 询问用户是替换还是追加
                if messagebox.askyesno("确认", "是否替换当前所有服务器配置?\n选择'否'将追加到现有配置中"):
                    self.servers = valid_servers
                else:
                    # 追加并去重
                    existing_keys = {(s["server"], s["port"]) for s in self.servers}
                    for server in valid_servers:
                        key = (server["server"], server["port"])
                        if key not in existing_keys:
                            self.servers.append(server)
                            existing_keys.add(key)
                
                self.update_server_tree()
                self.save_servers()
                messagebox.showinfo("成功", f"已成功导入 {len(valid_servers)} 个服务器配置")
        except Exception as e:
            messagebox.showerror("错误", f"导入配置失败: {e}")
    
    def on_server_select(self, event):
        selected_item = self.server_tree.selection()
        if not selected_item:
            return
        
        selected_index = int(selected_item[0])
        if 0 <= selected_index < len(self.servers):
            server = self.servers[selected_index]
            server_addr = server["server"]
            port = server["port"]
            self.display_server_logs(server_addr, port)
    
    def display_server_logs(self, server, port):
        # 清空日志显示
        self.log_text.config(state=tk.NORMAL)
        self.log_text.delete(1.0, tk.END)
        
        server_key = (server, port)
        if server_key in self.server_logs:
            logs = self.server_logs[server_key]
            if not logs:
                self.log_text.insert(tk.END, "暂无连接日志")
            else:
                # 按时间倒序显示日志
                for log in reversed(logs):
                    timestamp = log["timestamp"]
                    status = log["status"]
                    response_time = log["response_time"]
                    error = log["error"]
                    resolved_ip = log.get("resolved_ip", "未解析")
                    
                    # 设置日志颜色
                    if status == "在线":
                        status_color = "green"
                    else:
                        status_color = "red"
                    
                    self.log_text.insert(tk.END, f"{timestamp} - ", "timestamp")
                    self.log_text.insert(tk.END, f"{status} ", ("status", status_color))
                    self.log_text.insert(tk.END, f"({response_time})", "response_time")
                    
                    if resolved_ip:
                        self.log_text.insert(tk.END, f" - IP: {resolved_ip}", "ip")
                    
                    if error:
                        self.log_text.insert(tk.END, f" - 错误: {error}", "error")
                    
                    self.log_text.insert(tk.END, "\n")
        
        # 设置文本标签样式
        self.log_text.tag_configure("timestamp", foreground="blue")
        self.log_text.tag_configure("status", font=("SimHei", 10, "bold"))
        self.log_text.tag_configure("response_time", foreground="gray")
        self.log_text.tag_configure("error", foreground="red")
        self.log_text.tag_configure("ip", foreground="purple")
        
        # 滚动到底部
        self.log_text.see(tk.END)
        self.log_text.config(state=tk.DISABLED)
    
    def clear_logs(self):
        self.log_text.config(state=tk.NORMAL)
        self.log_text.delete(1.0, tk.END)
        self.log_text.config(state=tk.DISABLED)
    
    def parse_log_file(self, server, port):
        """从日志文件解析历史数据"""
        file_path = self.get_log_file_path(server, port)
        history = []
        
        try:
            if os.path.exists(file_path):
                with open(file_path, "r", encoding="utf-8") as f:
                    for line in f:
                        line = line.strip()
                        if not line:
                            continue
                            
                        # 解析时间戳
                        try:
                            timestamp_str = line.split(" - ")[0]
                            timestamp = datetime.strptime(timestamp_str, "%Y-%m-%d %H:%M:%S")
                        except ValueError:
                            continue  # 跳过无法解析的行
                        
                        # 解析状态
                        status_part = line.split(" - ")[1]
                        status = "在线" if "在线" in status_part else "离线"
                        
                        # 解析响应时间
                        try:
                            response_time_str = status_part.split("(")[1].split(")")[0]
                            response_time = float(response_time_str.split()[0])
                        except (IndexError, ValueError):
                            response_time = 0
                        
                        # 添加到历史数据
                        history.append({
                            "timestamp": timestamp,
                            "status": 1 if status == "在线" else 0,
                            "response_time": response_time
                        })
            
            # 按时间排序
            history.sort(key=lambda x: x["timestamp"])
            return history
        except Exception as e:
            print(f"解析日志文件失败: {e}")
            return []
    
    def show_reports(self):
        """显示服务器连接报表"""
        selected_item = self.server_tree.selection()
         
        if not selected_item:
            messagebox.showinfo("提示", "请先选择要查看报表的服务器")
            return
        
        selected_index = int(selected_item[0])
        if 0 <= selected_index < len(self.servers):
            server = self.servers[selected_index]
            server_addr = server["server"]
            port = server["port"]
            server_key = (server_addr, port)
            # history = self.parse_log_file(server_addr, port) 
            # self.server_history[server_key]=history
           
            # 检查是否有历史数据
            if server_key not in self.server_history or not self.server_history[server_key]:
                messagebox.showinfo("提示", "暂无历史数据可生成报表")
                return
            
            # 创建报表窗口
            report_window = tk.Toplevel(self.root)
            report_window.title(f"{server_addr}:{port} 连接报表")
            report_window.geometry("1000x700")
            report_window.transient(self.root)
            report_window.grab_set()
            
            # 计算窗口居中位置
            self.root.update_idletasks()
            x = self.root.winfo_x() + (self.root.winfo_width() // 2) - (1000 // 2)
            y = self.root.winfo_y() + (self.root.winfo_height() // 2) - (700 // 2)
            report_window.geometry(f"+{x}+{y}")
            
            # 创建选项卡
            notebook = ttk.Notebook(report_window)
            notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
            
            # 创建曲线图选项卡
            graph_frame = ttk.Frame(notebook)
            notebook.add(graph_frame, text="连接波动曲线")
            
            # 创建饼图选项卡
            pie_frame = ttk.Frame(notebook)
            notebook.add(pie_frame, text="连接状态统计")
            
            # 生成曲线图
            self.generate_connection_graph(graph_frame, server_key)
            
            # 生成饼图
            self.generate_status_pie_chart(pie_frame, server_key)
    
    def generate_connection_graph(self, parent, server_key):
        """生成连接波动曲线图"""
        server, port = server_key
        
        # 获取历史数据
        # history = self.server_history[server_key]
        history = self.parse_log_file(server, port) 
        if not history:
            ttk.Label(parent, text="暂无历史数据").pack(pady=20)
            return
        
        # 按时间排序
        history.sort(key=lambda x: x["timestamp"])
        
        # 准备数据
        timestamps = [entry["timestamp"] for entry in history]
        status = [entry["status"] for entry in history]
        response_times = [entry["response_time"] for entry in history]
        
        # 创建图表
        fig = Figure(figsize=(10, 6), dpi=100)
        
        # 添加响应时间子图
        ax1 = fig.add_subplot(211)
        ax1.plot(timestamps, response_times, 'b-', linewidth=1)
        ax1.set_title(f"{server}:{port} 响应时间波动")
        ax1.set_ylabel("响应时间 (ms)")
        ax1.grid(True)
        
        # 添加状态子图
        ax2 = fig.add_subplot(212)
        ax2.step(timestamps, status, 'g-', where='post')
        ax2.set_title(f"{server}:{port} 连接状态")
        ax2.set_ylabel("连接状态")
        ax2.set_ylim(-0.1, 1.1)
        ax2.set_yticks([0, 1])
        ax2.set_yticklabels(["离线", "在线"])
        ax2.grid(True)
        
        # 自动旋转x轴标签以避免重叠
        fig.autofmt_xdate()
        
        # 将图表放置到Tkinter窗口
        canvas = FigureCanvasTkAgg(fig, parent)
        canvas.draw()
        canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
    
    def generate_status_pie_chart(self, parent, server_key):
        """生成连接状态统计饼图"""
        server, port = server_key
        
        # 获取历史数据
        # history = self.server_history[server_key]
        
        history = self.parse_log_file(server, port) 
        if not history:
            ttk.Label(parent, text="暂无历史数据").pack(pady=20)
            return
        
        # 按小时统计
        hourly_data = {}
        for entry in history:
            hour = entry["timestamp"].hour
            if hour not in hourly_data:
                hourly_data[hour] = {"online": 0, "total": 0}
            hourly_data[hour]["total"] += 1
            if entry["status"] == 1:
                hourly_data[hour]["online"] += 1
        
        # 按天统计
        daily_data = {}
        for entry in history:
            day = entry["timestamp"].strftime("%Y-%m-%d")
            if day not in daily_data:
                daily_data[day] = {"online": 0, "total": 0}
            daily_data[day]["total"] += 1
            if entry["status"] == 1:
                daily_data[day]["online"] += 1
        
        # 创建选项卡
        pie_notebook = ttk.Notebook(parent)
        pie_notebook.pack(fill=tk.BOTH, expand=True)
        
        # 小时统计选项卡
        hourly_frame = ttk.Frame(pie_notebook)
        pie_notebook.add(hourly_frame, text="按小时统计")
        
        # 天统计选项卡
        daily_frame = ttk.Frame(pie_notebook)
        pie_notebook.add(daily_frame, text="按天统计")
        
        # 生成小时统计饼图
        if hourly_data:
            self.generate_pie_chart(hourly_frame, hourly_data, "按小时统计")
        else:
            ttk.Label(hourly_frame, text="暂无小时统计数据").pack(pady=20)
        
        # 生成天统计饼图
        if daily_data:
            self.generate_pie_chart(daily_frame, daily_data, "按天统计")
        else:
            ttk.Label(daily_frame, text="暂无天统计数据").pack(pady=20)
    
    def generate_pie_chart(self, parent, data, title):
        """生成饼图"""
        # 计算总在线率和离线率
        total_online = sum(item["online"] for item in data.values())
        total_checks = sum(item["total"] for item in data.values())
        total_offline = total_checks - total_online
        
        # 创建图表
        fig = Figure(figsize=(10, 6), dpi=100)
        ax = fig.add_subplot(111)
        
        # 绘制饼图
        labels = ['在线', '离线']
        sizes = [total_online, total_offline]
        colors = ['green', 'red']
        explode = (0.1, 0)  # 突出显示在线部分
        
        ax.pie(sizes, explode=explode, labels=labels, colors=colors,
               autopct='%1.1f%%', shadow=True, startangle=90)
        ax.axis('equal')  # 确保饼图是圆的
        ax.set_title(f"{title} - 总在线率: {total_online/total_checks*100:.1f}%")
        
        # 将图表放置到Tkinter窗口
        canvas = FigureCanvasTkAgg(fig, parent)
        canvas.draw()
        canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

if __name__ == "__main__":
    root = tk.Tk()
    app = NetworkMonitorApp(root)
    root.mainloop()    